﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using DotNetCommon.Extensions;

namespace DotNetCommon.Validate;

/// <summary>
/// 集合校验扩展
/// </summary>
public static class ValidateListExtensions
{
    #region NotNullOrEmpty 集合不能为空
    /// <summary>
    /// 集合不能为空
    /// </summary>
    public static IValidateContext<IEnumerable<T>> MustNotNullOrEmpty<T>(this IValidateContext<IEnumerable<T>> ctx, string errorMessage = null)
    {
        if (ctx.StopValidate) return ctx;
        if (!ctx.Model.IsNotNullOrEmpty())
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 不能为空!");
        }
        return ctx;
    }
    #endregion

    #region MustBeNullOrEmpty 集合必须为空
    /// <summary>
    /// 集合必须为空
    /// </summary>
    public static IValidateContext<IEnumerable<T>> MustBeNullOrEmpty<T>(this IValidateContext<IEnumerable<T>> ctx, string errorMessage = null)
    {
        if (ctx.StopValidate) return ctx;
        if (ctx.Model.IsNotNullOrEmpty())
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须为空!");
        }
        return ctx;
    }
    #endregion

    #region RuleForEach 集合遍历
    /// <summary>
    /// 如果集合不为空, 则遍历集合元素
    /// </summary>
    /// <remarks>
    /// 注意: 如果想限定集合不能为null, 可以在前面使用 MustNotNull, 如:
    /// <code>
    /// IEnumerable&lt;Person> list = null;
    /// var errorMsg = VValid.ValidErrorMessage(list, ctx =>
    /// {
    ///     ctx.MustNotNull().RuleForEach(ctx => {/*...*/});
    /// });
    /// //errorMsg: '$' 不能为空!
    /// </code>
    /// </remarks>
    public static void RuleForEach<T>(this IValidateContext<IEnumerable<T>> ctx, Action<IValidateContext<T>> validateAction)
    {
        if (ctx.StopValidate) return;
        if (ctx.Model == null) return;
        var idx = 0;
        foreach (var item in ctx.Model)
        {
            var propName = $"[{idx}]";
            idx++;
            var propModel = item;
            var newCtx = new ValidateContext<T>(propModel, propName, ctx);
            validateAction.Invoke(newCtx);
        }
    }

    /// <summary>
    /// 如果集合不为空, 则遍历集合元素
    /// </summary>
    /// <remarks>
    /// 注意: 如果想限定集合不能为null, 可以在前面使用 MustNotNull, 如:
    /// <code>
    /// IEnumerable&lt;Person> list = null;
    /// var errorMsg = VValid.ValidErrorMessageAsync(list, async(ctx, _) =>
    /// {
    ///     await ctx.MustNotNull().RuleForEachAsync((ctx, _) => {/*...*/});
    /// });
    /// //errorMsg: '$' 不能为空!
    /// </code>
    /// </remarks>
    public static async Task RuleForEachAsync<T>(this IValidateContext<IEnumerable<T>> ctx, Func<IValidateContext<T>, CancellationToken, Task> validateAction)
    {
        if (ctx.StopValidate) return;
        if (ctx.Model == null) return;
        var idx = 0;
        foreach (var item in ctx.Model)
        {
            var propName = $"[{idx}]";
            idx++;
            var propModel = item;
            var newCtx = new ValidateContext<T>(propModel, propName, ctx);
            await validateAction.Invoke(newCtx, newCtx.CancellationToken);
        }
    }
    #endregion

    #region RuleForEachAndEachNotNull 集合遍历
    /// <summary>
    /// 如果集合不为空, 则遍历集合元素; <br/>
    /// 如果集合内元素不为null, 则继续校验, 否则收集错误信息并返回;
    /// </summary>
    /// <remarks>
    /// 注意: 如果想限定集合不能为null, 可以在前面使用 MustNotNull
    /// </remarks>
    public static void RuleForEachAndEachMustNotNull<T>(this IValidateContext<IEnumerable<T>> ctx, Action<IValidateContext<T>> validateAction)
    {
        if (ctx.StopValidate) return;
        if (ctx.Model == null) return;
        var idx = 0;
        foreach (var item in ctx.Model)
        {
            var propName = $"[{idx}]";
            idx++;
            var propModel = item;
            var newCtx = new ValidateContext<T>(propModel, propName);
            ctx.Children.Add(newCtx);
            newCtx.Parent = ctx;
            newCtx.MustNotNull();
            if (newCtx.Model != null) validateAction.Invoke(newCtx);
        }
    }

    /// <summary>
    /// 如果集合不为空, 则遍历集合元素; <br/>
    /// 如果集合内元素不为null, 则继续校验, 否则收集错误信息并返回;
    /// </summary>
    /// <remarks>
    /// 注意: 如果想限定集合不能为null, 可以在前面使用 MustNotNull
    /// </remarks>
    public static async Task RuleForEachAndEachMustNotNullAsync<T>(this IValidateContext<IEnumerable<T>> ctx, Func<IValidateContext<T>, CancellationToken, Task> validateAction)
    {
        if (ctx.StopValidate) return;
        if (ctx.Model == null) return;
        var idx = 0;
        foreach (var item in ctx.Model)
        {
            var propName = $"[{idx}]";
            idx++;
            var propModel = item;
            var newCtx = new ValidateContext<T>(propModel, propName, ctx);
            newCtx.MustNotNull();
            if (newCtx.Model != null) await validateAction.Invoke(newCtx, newCtx.CancellationToken);
        }
    }
    #endregion
}
